using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using Unity.EditorCoroutines.Editor;
using static asap.lightmap.DLightmap;
using NUnit.Framework;
using UnityEditor.SceneManagement;
using System.IO;
using UnityEngine.Rendering;
using System.Linq;

namespace asap.lightmap
{
    [CustomEditor(typeof(DLightmap))]
    public class DLightmapInspector : Editor
    {
        private DLightmap dLightmap;
        private SerializedProperty root;

        public void OnEnable()
        {
            dLightmap = target as DLightmap;
            root = serializedObject.FindProperty("root");
        }

        public override void OnInspectorGUI()
        {
            serializedObject.Update();

            EditorGUI.BeginChangeCheck();
            EditorGUILayout.PropertyField(root, new GUIContent("root"));
            if (EditorGUI.EndChangeCheck())
            {
                serializedObject.ApplyModifiedProperties();
                Clear();
                serializedObject.ApplyModifiedProperties();
            }

            if (dLightmap.root == null)
            {
                EditorGUILayout.HelpBox("no root object", MessageType.Info);
                return;
            }
            if (!EditorApplication.isPlaying)
            {
                if (!PrefabUtility.IsPartOfAnyPrefab(dLightmap.root))
                {
                    EditorGUILayout.HelpBox("root is not part of prefab", MessageType.Error);
                    return;
                }
                if (!PrefabUtility.IsPartOfAnyPrefab(dLightmap.gameObject))
                {
                    EditorGUILayout.HelpBox($"DLightmap object {dLightmap.gameObject.name} is not part of prefab", MessageType.Error);
                    return;
                }
                if (PrefabUtility.GetOutermostPrefabInstanceRoot(dLightmap.root) !=
                        PrefabUtility.GetOutermostPrefabInstanceRoot(dLightmap.gameObject))
                {
                    EditorGUILayout.HelpBox($"DLightmap object {dLightmap.gameObject.name} root {dLightmap.root.name} in diff prefab", MessageType.Error);
                    return;
                }

                if (dLightmap.lightmapInfo == null
                        || dLightmap.lightmapInfo.lightmapDir == null
                        || dLightmap.lightmapInfo.lightmapDir.Length == 0)
                {
                    if (GUILayout.Button("Build"))
                    {
                        EditorCoroutineUtility.StartCoroutine(Build(), this);
                    }
                }
                else
                {
                    if (GUILayout.Button("Clear"))
                    {
                        Clear();
                    }
                }

            }
        }

        private IEnumerator Build()
        {
            var settings = Lightmapping.lightingSettings;
            if (settings == null)
            {
                Debug.LogError("No LightingSettings assigned");
                yield break;
            }
            if (settings.autoGenerate)
            {
                Debug.LogError("AutoGenerate should not be actived");
                yield break;
            }

            Lightmapping.BakeAsync();
            while (Lightmapping.isRunning) yield return null;
            EditorSceneManager.SaveScene(dLightmap.gameObject.scene);

            Debug.Log("Lightmap Baked");

            AssetDatabase.Refresh();

            yield return null;

            //Renderer[] renderers = null;
            //if (dLightmap.root == null)
            //renderers = FindObjectsOfType<Renderer>();
            //else
            //renderers = dLightmap.root.GetComponentsInChildren<Renderer>();

            Renderer[] renderers = dLightmap.root.GetComponentsInChildren<Renderer>();

            if (renderers == null || renderers.Length == 0)
            {
                Debug.LogError($"root {dLightmap.root.name} has no renderers");
                yield break;
            }

            var lightmapMode = LightmapSettings.lightmapsMode;
            var colorTextureList = new List<Texture2D>();
            var dirTextureList = new List<Texture2D>();
            var shadowMasks = new List<Texture2D>();
            var rendererInfos = new List<RendererInfo>();
            var lightmaps = LightmapSettings.lightmaps;

            if (lightmaps.Length == 0)
            {
                Debug.LogError("No Lightmap Datas");
            }
            else
            {
                Debug.Log($"Baked lightmap data lenght {lightmaps.Length}");
            }

            foreach (var renderer in renderers)
            {
                if (renderer.lightmapIndex != -1 && renderer.lightmapIndex != 65534)
                {
                    var info = new RendererInfo();
                    info.renderer = renderer;
                    info.lightmapOffsetScale = renderer.lightmapScaleOffset;
                    var originIndex = renderer.lightmapIndex;
                    var lightmapData = lightmaps[originIndex];
                    var lightmapColor = lightmapData.lightmapColor;
                    var newIndex = colorTextureList.IndexOf(lightmapColor);
                    if (newIndex == -1)
                    {
                        newIndex = colorTextureList.Count;
                        colorTextureList.Add(lightmapColor);
                    }
                    info.lightmapIndex = newIndex;

                    if (lightmapMode != LightmapsMode.NonDirectional)
                    {
                        var lightmapDir = lightmapData.lightmapDir;
                        var dirIndex = dirTextureList.IndexOf(lightmapDir);
                        if (dirIndex == -1)
                        {
                            dirIndex = dirTextureList.Count;
                            dirTextureList.Add(lightmapDir);
                            Assert.AreEqual(dirIndex, newIndex);
                        }
                    }

                    if (lightmapData.shadowMask != null)
                    {
                        var shadowMask = lightmapData.shadowMask;
                        var shadowMarskIndex = shadowMasks.IndexOf(shadowMask);
                        if (shadowMarskIndex == -1)
                        {
                            shadowMarskIndex = shadowMasks.Count;
                            shadowMasks.Add(shadowMask);
                            Assert.AreEqual(newIndex, shadowMarskIndex);
                        }
                    }

                    rendererInfos.Add(info);
                }
            }

            var dLightmapInfo = new DLightmapInfo()
            {
                lightmapsMode = lightmapMode,
                lightmapColor = colorTextureList.ToArray(),
                lightmapDir = dirTextureList.ToArray(),
                shadowMask = shadowMasks.ToArray(),
                rendererInfos = rendererInfos.ToArray()
            };

            var scenePath = EditorSceneManager.GetActiveScene().path;
            scenePath = scenePath.Substring(0, scenePath.Length - ".unity".Length);
            if (Directory.Exists(scenePath))
            {
                var reflectionProbs = dLightmap.GetComponentsInChildren<ReflectionProbe>();

                if (reflectionProbs != null || reflectionProbs.Length > 0)
                {
                    var reflectionProbInfos = new List<ReflectionProbeInfo>();

                    foreach (var probe in reflectionProbs)
                    {
                        if (probe.mode != ReflectionProbeMode.Baked)
                            continue;
                        var probeCubmap = probe.bakedTexture;
                        reflectionProbInfos.Add(new ReflectionProbeInfo()
                        {
                            probe = probe,
                            texture = probeCubmap
                        });
                    }

                    dLightmapInfo.reflectionInfos = reflectionProbInfos.ToArray();
                }
                else
                {
                    Debug.Log("No Reflection probes found");
                }

                if (RenderSettings.defaultReflectionMode == DefaultReflectionMode.Custom)
                {
                    dLightmapInfo.envReflection = RenderSettings.customReflection;
                }
                else
                {
                    var cubemaps = AssetDatabase.FindAssets(
                            $"t:{typeof(Cubemap).Name}",
                            new string[] { scenePath })
                        .Select(_ => AssetDatabase.LoadAssetAtPath<Cubemap>(AssetDatabase.GUIDToAssetPath(_)));
                    var allReflectionProbes = GameObject.FindObjectsOfType<ReflectionProbe>()
                        .Where(_ => _.mode == ReflectionProbeMode.Baked )
                        .Select(_ => _.bakedTexture);
                    var envReflection = cubemaps.Except(allReflectionProbes);
                    if(envReflection.Count() != 1)
                    {
                        Debug.LogError("Cubemap Num not equel ReflectionProbe Num+1");
                    }
                    else
                    {
                        dLightmapInfo.envReflection = (Cubemap)envReflection.First();
                    }
                }

            }
            else
            {
                Debug.LogError($"Scene {EditorSceneManager.GetActiveScene().name} Dir is not found");
                yield break;
            }

            dLightmapInfo.lightProbes = LightmapSettings.lightProbes.bakedProbes;

            dLightmap.lightmapInfo = dLightmapInfo;
            serializedObject.ApplyModifiedProperties();
            var prefabRoot = PrefabUtility.GetOutermostPrefabInstanceRoot(dLightmap.gameObject);
            PrefabUtility.ApplyPrefabInstance(prefabRoot, InteractionMode.AutomatedAction);
            AssetDatabase.Refresh();
        }

        private void Clear()
        {
            dLightmap.lightmapInfo = null;
            Lightmapping.Clear();
            Lightmapping.ClearLightingDataAsset();
            AssetDatabase.Refresh();
        }
    }
}
